package de.fuberlin.simpleoffloading;

import java.io.IOException;
import java.io.StringReader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import android.os.AsyncTask;
import android.os.BatteryManager;
import android.os.Bundle;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.view.Menu;
import android.widget.EditText;
import android.widget.TextView;
import android.view.View;

public class SimpleOffloading extends Activity {
	
	private TextView tvAndroidTime;
	private TextView tvServerTime;
	private TextView tvRealServerTime;
	private TextView tvAndroidBattery; 
	private TextView tvServerBattery;
	private TextView tvCurrentBattery;
	private EditText etMillionsIters;
	private long startServerTime;
	private double startServerBattery;
	private double currentBattery; // In %

	@Override
    protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple_offloading);
        currentBattery = -1;
        tvServerTime = (TextView) findViewById(R.id.textView7);
        tvAndroidTime = (TextView) findViewById(R.id.textView2);
        tvRealServerTime = (TextView) findViewById(R.id.textView13);
        tvServerBattery = (TextView) findViewById(R.id.textView12);
        tvAndroidBattery = (TextView) findViewById(R.id.textView3);
        tvCurrentBattery = (TextView) findViewById(R.id.textView15);
        etMillionsIters = (EditText) findViewById(R.id.editText1);
        keepBatteryUpdated();
     }
	
	public void keepBatteryUpdated() {
		BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
	        @Override
	        public void onReceive(Context context, Intent intent) {
	            double level =  (double) intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
	            double scale =  (double) intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
	            currentBattery = (level/scale)*100.0; // We calculate the remaining % of the battery
	            // But this will not be precise enough to see the consumed battery by a given algorithm
	            // The original indicators level and scale are also not precise enough
	            tvCurrentBattery.setText(Double.toString(currentBattery));
	            tvCurrentBattery.setVisibility(View.VISIBLE);
	        }
	    };
	    IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
	    registerReceiver(batteryReceiver, filter);
	 }
	
	@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_simple_offloading, menu);
        return true;
    }
	
	public void onClickedTestIt(View view) {
		runAndroid(); //This first, because otherwise, when the separate thread that uses runServer ends, could interrupt runAndroid with the onPostExecute() function
		runServer(); //Retrieve an XML formatted answer from a server and show it
	}
	
	private void runAndroid(){
		long startTime = System.currentTimeMillis();
		double startBattery = currentBattery;
		long nLoops = Long.parseLong(etMillionsIters.getText().toString()) * 1000000;
		long i = 0;
		while (i < nLoops) i++;
		long takenTime = System.currentTimeMillis() - startTime;
		double takenBattery = currentBattery - startBattery; // The difference is too low, it will be almost always 0.
		tvAndroidTime.setText(Long.toString(takenTime));
		tvAndroidBattery.setText(Double.toString(takenBattery));
		tvAndroidTime.setVisibility(View.VISIBLE);
		tvAndroidBattery.setVisibility(View.VISIBLE);
	}
	
	private void runServer(){
		startServerTime = System.currentTimeMillis();
		startServerBattery = currentBattery;
		String nLoops = etMillionsIters.getText().toString();
		new GetServerData().execute(getString(R.string.serverURL) + "?nLoops=" + nLoops);
	}
	
	private class GetServerData extends AsyncTask<String, Void, String> {
		@Override
        protected String doInBackground(String... urlAddress) {
        	try {
    			DefaultHttpClient httpClient = new DefaultHttpClient();
    			HttpGet httpGet = new HttpGet(urlAddress[0]);
    			ResponseHandler<String> resHandler = new BasicResponseHandler();
    			String responseData = httpClient.execute(httpGet, resHandler);
    			return responseData;
    		} catch (Exception e) {
    			e.printStackTrace();
    			return "Error";
    		}
        }
        @Override
        protected void onPostExecute(String result) {
        	if (result.equals("Error")) tvServerTime.setText("Unable to retrieve the data, check your connection. URL may be invalid.");
        	else {
        		long takenTime = System.currentTimeMillis() - startServerTime;
        		double takenBattery = currentBattery - startServerBattery; // The difference is too low, it will be almost always 0.
        		tvServerTime.setText(Long.toString(takenTime));
        		tvServerBattery.setText(Double.toString(takenBattery));
        		try {
					String serverExecutionTime = getElementValueFromXML(result, "executionTime");
					tvRealServerTime.setText(serverExecutionTime);
        			tvRealServerTime.setVisibility(View.VISIBLE);
        		} catch (Exception e) {
	        		e.printStackTrace();
	        	}
        	}
        	tvServerTime.setVisibility(View.VISIBLE);
        	tvServerBattery.setVisibility(View.VISIBLE);
       }
    }
    
	private static String getElementValueFromXML(String xmlString, String tagName) 
			throws ParserConfigurationException, SAXException, IOException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		InputSource is = new InputSource();
		is.setCharacterStream(new StringReader(xmlString));
		Document doc = db.parse(is);
		NodeList summary = doc.getElementsByTagName(tagName);
		Element line = (Element) summary.item(0);
		return getCharacterDataFromElement(line);
	}
	
	private static String getCharacterDataFromElement(Element e) {
		Node child = e.getFirstChild();
		if (child instanceof CharacterData) {
			CharacterData cd = (CharacterData) child;
			return cd.getData();
		}
		return "";
	}
	  
}
